Hello, > I posted this to sun-managers, but it has some nasty consequences if deliberately > exploited. If anyone has any more info, or ideas for a fix, please let me know. The internet draft draft-heavens-problems-rsts-00.txt describes the problem in detail. In short, TCP doesn't handle RSTs correctly during the open and close negotiations of a connection. > It concerns me that one remote site can so easily completely block all > incoming tcp/ip connections on a port. Is this a kernel bug, or something > I can take some measure to prevent on this end? TCP is a second generation protocol -- it's buggy. This is but one of several such problems. > If anyone has any more specifics on this problem, please let me know. When > the server is healthy netstat indicates a couple SYN_RCVD state services, but > they never last from one netstat command to another for the same remote IP. I'm including a program that I use to clean out these "stuck" connections. It also keeps us from having to reboot our web server once a week. One caveat is that I haven't seen a SYN_RCVD connection stuck, so your mileage may vary on that one. The others work like a charm. Andrew Gross -- Andrew Gross (grossa@sdsc.edu) | Information | Voice: +1.619.534.5086 San Diego Supercomputer Center | Security | FAX: +1.619.534.5152 P. O. Box 85608 | Researcher | Quis custodiet ipsos custodes. San Diego CA 92186-9784 | | --Juvenal, _Satires_, VI, 347 ========================================================================= #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # socket_clear.c # This archive created: Fri Oct 27 09:48:53 1995 export PATH; PATH=/bin:$PATH if test -f 'socket_clear.c' then echo shar: will not over-write existing file "'socket_clear.c'" else cat << \SHAR_EOF > 'socket_clear.c' /* * Andrew Gross 30 Sep 1995 (grossa@sdsc.edu) [SunOS 4.1.4] * * This program will help out on WWW and anon-ftp servers by clearing * out "stuck" sockets. * * This program when compiled as (cc .c -lkvm) prints out information * on sockets in the closing states. * * When compiled as (cc .c -DFIX -lkvm), it goes and "helps out" * sockets stuck in closing states. Sockets in CLOSE_WAIT, LAST_ACK, * FIN_WAIT_1, and CLOSING with the TCPT_REXMT and TCPT_2MSL timers off are * moved into TIME_WAIT and the timers TCPT_PERSIST and TCPT_KEEP are * cleared. The concern here is that soisdisconnected() get called to * clear out a possible waiting process and this happens correctly * when the TCPT_2MSL timer goes off. * * * These are the differences on a WWW server from a netstat -m before (<) * and after (>) running the fixing version of this code. * * < 2191/3648 mbufs in use: * < 923 mbufs allocated to data * < 119 mbufs allocated to packet headers * < 244 mbufs allocated to socket structures * < 387 mbufs allocated to protocol control blocks * --- * > 1445/3648 mbufs in use: * > 360 mbufs allocated to data * > 72 mbufs allocated to packet headers * > 202 mbufs allocated to socket structures * > 293 mbufs allocated to protocol control blocks * * < 474/592 cluster buffers in use * < 1048 Kbytes allocated to network (71% in use) * --- * > 157/368 cluster buffers in use * > 824 Kbytes allocated to network (40% in use) * * 20 October 1995 * The internet draft draft-heavens-problems-rsts-00.txt describes the * problem that this code combats. * */ #include <stdio.h> #include <nlist.h> #include <kvm.h> #include <sys/types.h> #include <sys/fcntl.h> #include <sys/socket.h> #include <sys/socketvar.h> #include <net/route.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/in_pcb.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <netinet/icmp_var.h> #include <netinet/ip_var.h> #include <netinet/tcp.h> #include <netinet/tcpip.h> #include <netinet/tcp_seq.h> #define TCPSTATES #include <netinet/tcp_fsm.h> #include <netinet/tcp_timer.h> #include <netinet/tcp_var.h> #include <netinet/tcp_debug.h> struct nlist nl[] = { #define N_TCB 0 { "_tcb" }, "", }; struct inpcb pcb; struct tcpcb tcb; main() { kvm_t *kd; struct inpcb *curr,*prev=NULL,*next,*orig; struct socket soc; int i; if(!(kd = kvm_open(NULL, NULL, NULL, O_RDWR, NULL))) { perror("kvm_open"); exit(1); } if(kvm_nlist(kd, nl)) { perror("kvm_nlist"); exit(1); } curr = orig = (struct inpcb *) nl[N_TCB].n_value; do { if((kvm_read(kd, (u_long) curr, &pcb, sizeof pcb) != sizeof pcb) || (prev && (prev != pcb.inp_prev))) { perror("read current"); exit(1); } if(!pcb.inp_ppcb) continue; if((kvm_read(kd, (u_long) pcb.inp_ppcb, &tcb, sizeof tcb) != sizeof tcb) || (curr != tcb.t_inpcb)) { perror("read tcpcb"); exit(1); } #ifndef FIX if((kvm_read(kd, (u_long) pcb.inp_socket, &soc, sizeof soc)!= sizeof soc) || (curr != (struct inpcb *) soc.so_pcb)) { perror("read soc"); exit(1); } #endif /* only deal with closing... */ /* * T2&& (possibly) T1 && (close_wait||last_ack||closing||fin_wait_1) */ #ifdef FIX if ( tcb.t_state != SYN_RCVD && tcb.t_state != TCPS_LAST_ACK && tcb.t_state != TCPS_CLOSE_WAIT && tcb.t_state != TCPS_FIN_WAIT_1 && tcb.t_state != TCPS_CLOSING ) continue; if ( tcb.t_timer[TCPT_REXMT] !=0 || tcb.t_timer[TCPT_2MSL] !=0 ) continue; #else /* skip uninteresting states: up to est. and normal finish */ if ( tcb.t_state <= TCPS_ESTABLISHED || tcb.t_state == TCPS_TIME_WAIT && tcb.t_timer[TCPT_2MSL] != 0) continue; #endif printf("s=%s %d ", inet_ntoa(pcb.inp_laddr), pcb.inp_lport); printf("d=%s %d ", inet_ntoa(pcb.inp_faddr), pcb.inp_fport); if (tcb.t_state < 0 || tcb.t_state >= TCP_NSTATES) printf("st=%d", tcb.t_state); else printf("st=%s", tcpstates[tcb.t_state]); #ifndef FIX printf(" so_st=%d ",soc.so_state); /* printf("ka=%d ",soc.so_state&SO_KEEPALIVE); printf(" ling=%d ",soc.so_state&SO_LINGER); */ printf("wupalt=%p",soc.so_wupalt); #endif printf("\n"); for (i=0; i<TCPT_NTIMERS; i++) printf("T(%d)= %5d ",i,tcb.t_timer[i]); printf("\n"); #ifdef FIX if ( tcb.t_timer[TCPT_KEEP] > 3 && ( tcb.t_timer[TCPT_PERSIST] == 0 || tcb.t_timer[TCPT_PERSIST] > 3 ) ) { /* * Get out of wait free: * Since tcp_close calls soisdisconnected we can do this * as that call is need to transition to CLOSE_WAIT . * */ tcb.t_state = TCPS_TIME_WAIT; tcb.t_timer[TCPT_PERSIST] = 0; tcb.t_timer[TCPT_KEEP] = 0; tcb.t_timer[TCPT_2MSL] = 10; if((kvm_write(kd, (u_long) pcb.inp_ppcb, &tcb, sizeof tcb) != sizeof tcb) ) { perror("kvm_write"); exit(1); } } #endif } while ( prev = curr, curr = pcb.inp_next, curr != orig ); } SHAR_EOF fi # end of overwriting check # End of shell archive exit 0